#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <sqlite3.h>
#import <UIKit/UIKit.h>
#import <UIKit/UIApplication.h>
#import <SpringBoard/SBApplication.h>
#import <SpringBoard/SBApplicationController.h>
#include "../Plugin.h"

extern "C" CFStringRef UIDateFormatStringForFormatType(CFStringRef type);

#define localize(bundle, str) \
	[bundle localizedStringForKey:str value:str table:nil]

static SBApplication* getApp()
{
	Class cls = objc_getClass("SBApplicationController");
	SBApplicationController* ctr = [cls sharedInstance];

	SBApplication* app = [ctr applicationWithDisplayIdentifier:@"com.olefsky.toodledo"];

	return app;
}

@interface DotView : UIView

@property (nonatomic, retain) UIColor* color;

@end

@implementation DotView

@synthesize color;

-(void) drawRect:(CGRect) rect
{
	CGContextRef ctx = UIGraphicsGetCurrentContext();
	[self.color set];
	CGContextFillEllipseInRect(ctx, rect);

	NSBundle* b = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/CalendarUI.framework"];
	NSString* path = [b pathForResource:@"dotshine" ofType:@"png"];
	UIImage* image = [UIImage imageWithContentsOfFile:path];
	[image drawInRect:rect];
}
@end

@interface ToodledoView : UIView

@property (nonatomic, retain) DotView* dot;
@property (nonatomic, retain) LILabel* name;
@property (nonatomic, retain) LILabel* due;
@property (nonatomic, retain) UIImageView* priority;

@end

@implementation ToodledoView

@synthesize dot, due, name, priority;

@end

static ToodledoView* createView(CGRect frame, LITableView* table)
{
	ToodledoView* v = [[[ToodledoView alloc] initWithFrame:frame] autorelease];
	v.backgroundColor = [UIColor clearColor];

	v.dot = [[[DotView alloc] initWithFrame:CGRectMake(4, 4, 9, 9)] autorelease];
	v.dot.backgroundColor = [UIColor clearColor];
	
	v.name = [table labelWithFrame:CGRectZero];
	v.name.frame = CGRectMake(22, 0, 275, 16);
	v.name.backgroundColor = [UIColor clearColor];

	v.due = [table labelWithFrame:CGRectZero];
	v.due.frame = CGRectMake(22, 16, 275, 14);
	v.due.backgroundColor = [UIColor clearColor];

	v.priority = [[[UIImageView alloc] initWithFrame:CGRectMake(305, 3, 10, 10)] autorelease];
	v.priority.backgroundColor = [UIColor clearColor];

	[v addSubview:v.dot];
	[v addSubview:v.due];
	[v addSubview:v.name];
	[v addSubview:v.priority];

	return v;
}


@interface ToodledoPlugin : NSObject <LIPluginController, LITableViewDelegate, UITableViewDataSource> 
{
	NSTimeInterval lastUpdate;
}

@property (nonatomic, retain) LIPlugin* plugin;
@property (retain) NSDictionary* todoPrefs;
@property (retain) NSArray* todoList;

@property (retain) NSString* sql;
@property (retain) NSString* prefsPath;
@property (retain) NSString* dbPath;

@end

@implementation ToodledoPlugin

@synthesize todoList, todoPrefs, sql, plugin, prefsPath, dbPath;

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{
	return self.todoList.count;
}

- (UITableViewCell *)tableView:(LITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ToodledoCell"];
	
	if (cell == nil) {
		cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"ToodledoCell"] autorelease];
		//cell.backgroundColor = [UIColor clearColor];
		
		ToodledoView* v = createView(CGRectMake(0, 0, 320, 35), tableView);
		v.tag = 57;
		[cell.contentView addSubview:v];
	}
	
	ToodledoView* v = [cell.contentView viewWithTag:57];
	v.name.style = tableView.theme.summaryStyle;
	v.due.style = tableView.theme.detailStyle;
	
	NSDictionary* elem = [self.todoList objectAtIndex:indexPath.row];
	v.name.text = [elem objectForKey:@"name"];

	BOOL ind = true;
	if (NSNumber* b = [self.todoPrefs objectForKey:@"ShowListColors"])
		ind = b.boolValue;
	
	
	//
	// DATE DISPLAY
	//
	
	// Create a variable that will tell if this is the first item on the line,
	// so if there is already something on this line it will add a " - " to space out
	// the items.
	// There will be a better way of doing this so I will improve this at a later date
	bool firstItem = true;

	// Check if date should be shown or not
	bool displayDate = false;
	if (NSNumber* n = [self.plugin.preferences valueForKey:@"ShowDueDate"])
		displayDate = n.boolValue;
		
	if (displayDate)
	{
		NSNumber* dateNum = [elem objectForKey:@"due"];
		if (dateNum.doubleValue == 0)
		{
			NSBundle* bundle = [NSBundle bundleForClass:[self class]];
			v.due.text = localize(bundle, @"No Due Date");
		}
		else
		{
			int TZoffset = [[NSTimeZone localTimeZone] secondsFromGMT];
			//NSLog(@"LI:Toodledo: TZOffset %d", TZoffset);
			NSDate* date = [[[NSDate alloc] initWithTimeIntervalSince1970:dateNum.doubleValue-TZoffset] autorelease];
			NSDateFormatter* df = [[[NSDateFormatter alloc] init] autorelease];
			df.dateFormat = (NSString*)UIDateFormatStringForFormatType(CFSTR("UIAbbreviatedWeekdayMonthDayTimeFormat"));
			v.due.text = [df stringFromDate:date];
		}
		
		firstItem = false;				// Update variable to show there is now text on this line
	}
	
	//
	// FOLDER DISPLAY
	//
	bool displayfolder = false;
	if (NSNumber* n = [self.plugin.preferences valueForKey:@"DisplayFolder"])
		displayfolder = n.boolValue;
	
	if (displayfolder)
	{
		NSLog(@"LI:Toodledo: Displaying folder");
		NSString* folderName = [elem objectForKey:@"folder"];
		
		if (folderName.length == 0)
			folderName = [NSString stringWithFormat:@"No Folder"];
			
		if (firstItem == false)
			v.due.text = [NSString stringWithFormat:@"%@ - %@", v.due.text, folderName];
		else
			v.due.text = [NSString stringWithFormat:@"%@", folderName];
			
		firstItem = false;				// Update variable to show there is now text on this line
	}
	
	
	// 
	// CONTEXT DISPLAY
	//
	bool displaycontext = false;
	if (NSNumber* n = [self.plugin.preferences valueForKey:@"DisplayContext"])
		displaycontext = n.boolValue;
	
	if (displaycontext)
	{
		NSLog(@"LI:Toodledo: Displaying context");
		NSString* contextName = [elem objectForKey:@"context"];
		
		if (contextName.length == 0)
			contextName = [NSString stringWithFormat:@"No Context"];
			
		if (firstItem == false)
			v.due.text = [NSString stringWithFormat:@"%@ <%@>", v.due.text, contextName];
		else
			v.due.text = [NSString stringWithFormat:@"<%@>", contextName];
			
		firstItem = false;				// Update variable to show there is now text on this line
	}
	
	
	// 
	// HIGHLIGHTING OF STARRED TASKS & COLOURING OF SPOTS
	//	

	// See if user wants to highlight starred tasks
	bool highlightstarred = false;
	if (NSNumber* n = [self.plugin.preferences valueForKey:@"HighlightStarred"])
		highlightstarred = n.boolValue;
		
	// If item is starred, change spot colour
	bool isStarred = false;
	if ([[elem objectForKey:@"starred"] intValue] == 1 && highlightstarred == true)
		isStarred = true;
		
	if (isStarred)
	{
		NSLog(@"LI:Toodledo: Item IS starred");
		UIColor* color = [UIColor colorWithRed:1.0
					green:1.0
					blue:0.0
					alpha:1];
		v.dot.color = color;
		v.dot.hidden = false;
		[v.dot setNeedsDisplay];
	}
	else
	{
		NSLog(@"LI:Toodledo: Item is NOT starred");
		UIColor* color = [UIColor colorWithRed:0.5
					green:0.5
					blue:0.5
					alpha:1];
		v.dot.color = color;
		v.dot.hidden = false;
		[v.dot setNeedsDisplay];
	}

	
	return cell;

}

- (id) initWithPlugin:(LIPlugin*) plugin
{
	self = [super init];
	self.plugin = plugin;
	
	plugin.tableViewDataSource = self;
	plugin.tableViewDelegate = self;

	NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
	[center addObserver:self selector:@selector(update:) name:LITimerNotification object:nil];
	[center addObserver:self selector:@selector(update:) name:LIViewReadyNotification object:nil];

	return self;
}

- (void) updateTasks
{
	if (self.dbPath == nil)
	{
		SBApplication* app = getApp();
		NSString* appPath = [app.path stringByDeletingLastPathComponent];
		self.dbPath = [[appPath stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"toodledo21.sql"];
		self.prefsPath = [appPath stringByAppendingFormat:@"/Library/Preferences/%@.plist", app.displayIdentifier];
	}

	self.todoPrefs = [NSDictionary dictionaryWithContentsOfFile:self.prefsPath];
//	NSLog(@"LI:Toodledo: Prefs: %@: %@", self.prefsPath, self.todoPrefs);
	
	//NSString *allSql = @"select tasks.name, tasks.due_date, tasks.priority, lists.color from tasks left outer join lists on lists.pk = tasks.list where tasks.completion_date < 0 and tasks.deleted = 0";
	NSString *allSql = @"SELECT "
		"tasks.title, CASE WHEN duedate=0 then 0 WHEN duedate>0 and duetime=0 THEN duedate-43200 WHEN duedate>0 and duetime>0 then ((duedate - 43200) + (strftime('%s',time(duetime,'unixepoch')) - 946684800)) ELSE 0 END AS 'duetime', tasks.priority, tasks.starred, folders.name, contexts.name FROM (tasks LEFT JOIN folders on tasks.folder = folders.pk) LEFT JOIN contexts on tasks.context = contexts.pk WHERE ((completed=0)";
	
	
	// Lowest priority to show
	int lowestPriority = 99;
	if (NSNumber* n = [self.plugin.preferences valueForKey:@"LowestPriority"])
		lowestPriority = n.intValue;
	if (lowestPriority < 99)
		allSql = [NSString stringWithFormat:@"%@ AND ( ( priority >= %i ", allSql, lowestPriority];
		
	// Hide deferred?
	BOOL hideDeferred = false;
	if (NSNumber* n = [self.plugin.preferences valueForKey:@"HideDeferred"])
		hideDeferred = n.boolValue;
	if (hideDeferred)
		allSql = [allSql stringByAppendingString:@" AND status <= 3 "];
	
	// Hide without due date?
	BOOL hideNoDueDate = false;
	if (NSNumber* n = [self.plugin.preferences valueForKey:@"HideNoDueDate"])
		hideNoDueDate = n.boolValue;
	if (hideNoDueDate)
		allSql = [allSql stringByAppendingString:@" AND duedate > 0 "];
	
	
	// Close brackets
	allSql = [allSql stringByAppendingString:@" ) "];
	
	// Show Starred 
	BOOL showStarred = false;
	if (NSNumber* n = [self.plugin.preferences valueForKey:@"ShowStarred"])
			showStarred = n.boolValue;
	if (showStarred)
		allSql = [allSql stringByAppendingString:@" OR (starred = 1) "];
		
	// Close brackets
	allSql = [allSql stringByAppendingString:@" ) "];
	
	
	// Hide or show Future dated tasks (i.e. startdate > today)
	BOOL hideFuture = false;
	if (NSNumber* n = [self.plugin.preferences valueForKey:@"HideFuture"])
		hideFuture = n.boolValue;
	if (hideFuture)
		allSql = [allSql stringByAppendingString:@" AND strftime('%s','now') - ((startdate - 43200) + (strftime('%s',time(starttime,'unixepoch')) - 946684800)) >=0 "];
		
	
	// Close of SQL statement
	allSql = [allSql stringByAppendingString:@" ) "];
	
	
	



	//NSString* sql = [NSString stringWithFormat:@"%@ order by tasks.due_date ASC limit %i", allSql, queryLimit];
	NSString* sql = [NSString stringWithFormat:@"%@ order by case when duedate=0 then 2000000000 else duedate end, priority DESC ", allSql];
	NSLog(@"LI:Toodledo: Executing SQL: %@", sql);
			
	/* Get the todo database timestamp */
	NSFileManager* fm = [NSFileManager defaultManager];
	NSDictionary *dataFileAttributes = [[NSFileManager defaultManager] fileAttributesAtPath:self.dbPath traverseLink:YES];
	NSDate* lastDataModified = [dataFileAttributes objectForKey:NSFileModificationDate];
	
	if(![sql isEqualToString:self.sql] || lastUpdate < lastDataModified.timeIntervalSinceReferenceDate)
	{
		NSLog(@"LI:Toodledo: Loading Todo Tasks...");
		self.sql = sql;

		// Update data and read from database
		NSMutableArray *todos = [NSMutableArray arrayWithCapacity:4];
		
		sqlite3 *database = NULL;
		@try
		{		
			if (sqlite3_open([self.dbPath UTF8String], &database) != SQLITE_OK) 
			{
				NSLog(@"LI:Toodledo: Failed to open database.");
				return;
			}

			// Setup the SQL Statement and compile it for faster access
			sqlite3_stmt *compiledStatement = NULL;

			@try
			{
				if (sqlite3_prepare_v2(database, [sql UTF8String], -1, &compiledStatement, NULL) != SQLITE_OK) 
				{
					NSLog(@"LI:Toodledo: Failed to prepare statement: %s", sqlite3_errmsg(database));
					return;
				}
		
						
				// Loop through the results and add them to the feeds array
				while(sqlite3_step(compiledStatement) == SQLITE_ROW) 
				{
					const char *cText = (const char*)sqlite3_column_text(compiledStatement, 0);
					double cDue  = sqlite3_column_double(compiledStatement, 1);
					int priority  = sqlite3_column_int(compiledStatement, 2);
					int starred = sqlite3_column_int(compiledStatement, 3);
					const char *cFolder = (const char*)sqlite3_column_text(compiledStatement, 4);
					const char *cContext = (const char*)sqlite3_column_text(compiledStatement, 5);
					
					NSString *aText = [NSString stringWithUTF8String:(cText == NULL ? "" : cText)];
					NSString *aFolder = [NSString stringWithUTF8String:(cFolder == NULL ? "" : cFolder)];
					NSString *aContext = [NSString stringWithUTF8String:(cContext == NULL ? "" : cContext)];
				
							
					NSDictionary *todoDict = [NSDictionary dictionaryWithObjectsAndKeys:
						aText, @"name",
						[NSNumber numberWithDouble:cDue], @"due",
						[NSNumber numberWithInt:priority], @"priority", 
						[NSNumber numberWithInt:starred], @"starred",
						aFolder, @"folder",
						aContext, @"context",
						nil];
				
					[todos addObject:todoDict];
				}
			}
			@finally
			{			
				if (compiledStatement != NULL)
					sqlite3_finalize(compiledStatement);
			}
		}
		@finally
		{
			if (database != NULL)
				sqlite3_close(database);
		}
	
		[self performSelectorOnMainThread:@selector(setTodoList:) withObject:todos waitUntilDone:YES];	

		// Inside on SMS and outside on Weather Info.  This is likely location of SB crash
		NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:1];
		[dict setObject:todos forKey:@"todos"];  
		[[NSNotificationCenter defaultCenter] postNotificationName:LIUpdateViewNotification object:self.plugin userInfo:dict];
		
		lastUpdate = lastDataModified.timeIntervalSinceReferenceDate;
	}
}

- (void) update:(NSNotification*) notif
{
	if (!self.plugin.enabled)
		return;

	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
	[self updateTasks];
	[pool release];
}

@end
